1# Attachments Tutorial
   2
   3<!-- TOC -->
   4
   5## Attachables
   6BOSL2 introduces the concept of attachables.  You can do the following
   7things with attachable shapes:
   8
   9* Control where the shape appears and how it is oriented by anchoring and specifying orientation and spin
  10* Position or attach shapes relative to parent objects
  11* Tag objects and then control boolean operations based on their tags.
  12* Change the color of objects so that child objects are different colors than their parents
  13
  14The various attachment features may seem complex at first, but 
  15attachability is one of the most important features of the BOSL2
  16library.  It enables you to position objects relative to other objects
  17in your model instead of having to keep track of absolute positions.
  18It makes models simpler, more intuitive, and easier to maintain.
  19
  20Almost all objects defined by BOSL2 are attachable.  In addition,
  21BOSL2 overrides the built-in definitions for `cube()`, `cylinder()`,
  22`sphere()`, `square()`, `circle()` and `text()` and makes them attachable as
  23well.  However, some basic OpenSCAD built-in definitions are not
  24attachable and will not work with the features described in this
  25tutorial.  The non-attachables are `polyhedron()`, `linear_extrude()`,
  26`rotate_extrude()`, `surface()`, `projection()` and `polygon()`.
  27Some of these have attachable alternatives: `vnf_polyhedron()`,
  28`linear_sweep()`, `rotate_sweep()`, and `region()`.  
  29
  30
  31## Anchoring
  32Anchoring allows you to align a specified part of an object or point
  33on an object with the origin.  The alignment point can be the center
  34of a side, the center of an edge, a corner, or some other
  35distinguished point on the object.  This is done by passing a vector
  36or text string into the `anchor=` argument.  For roughly cubical
  37or prismoidal shapes, that vector points in the general direction of the side, edge, or
  38corner that will be aligned to.  For example, a vector of [1,0,-1] refers to the lower-right
  39edge of the shape.  Each vector component should be -1, 0, or 1:
  40
  41```openscad-3D
  42include <BOSL2/std.scad>
  43// Anchor at upper-front-left corner
  44cube([40,30,50], anchor=[-1,-1,1]);
  45```
  46
  47```openscad-3D
  48include <BOSL2/std.scad>
  49// Anchor at upper-right edge
  50cube([40,30,50], anchor=[1,0,1]);
  51```
  52
  53```openscad-3D
  54include <BOSL2/std.scad>
  55// Anchor at bottom face
  56cube([40,30,50], anchor=[0,0,-1]);
  57```
  58
  59Since manually written vectors are not very intuitive, BOSL2 defines some standard directional
  60vector constants that can be added together:
  61
  62Constant | Direction | Value
  63-------- | --------- | -----------
  64`LEFT`   | X-        | `[-1, 0, 0]`
  65`RIGHT`  | X+        | `[ 1, 0, 0]`
  66`FRONT`/`FORWARD`/`FWD` | Y− | `[ 0, −1, 0]`
  67`BACK`   | Y+        | `[ 0, 1, 0]`
  68`BOTTOM`/`BOT`/`DOWN` | Z− (Y− in 2D) | `[ 0, 0, −1]` (`[0, −1]` in 2D.)
  69`TOP`/`UP` | Z+ (Y+ in 2D)      | `[ 0, 0, 1]` (`[0, 1]` in 2D.)
  70`CENTER`/`CTR` | Centered | `[ 0, 0, 0]`
  71
  72If you want a vector pointing towards the bottom−left edge, just add the `BOTTOM` and `LEFT` vector
  73constants together like `BOTTOM + LEFT`.  This will result in a vector of `[−1,0,−1]`.  You can pass
  74that to the `anchor=` argument for a clearly understandable anchoring:  
  75
  76```openscad-3D
  77include <BOSL2/std.scad>
  78cube([40,30,50], anchor=BACK+TOP);
  79```
  80
  81```openscad-3D
  82include <BOSL2/std.scad>
  83cube([40,30,50], anchor=FRONT);
  84```
  85
  86---
  87
  88For cylindrical type attachables, the Z component of the vector will be −1, 0, or 1, referring
  89to the bottom rim, the middle side, or the top rim of the cylindrical or conical shape.
  90The X and Y components can be any value, pointing towards the circular perimeter of the cone.
  91These combined let you point at any place on the bottom or top rims, or at an arbitrary
  92side wall:
  93
  94```openscad-3D
  95include <BOSL2/std.scad>
  96cylinder(r1=25, r2=15, h=60, anchor=TOP+LEFT);
  97```
  98
  99```openscad-3D
 100include <BOSL2/std.scad>
 101cylinder(r1=25, r2=15, h=60, anchor=BOTTOM+FRONT);
 102```
 103
 104```openscad-3D
 105include <BOSL2/std.scad>
 106cylinder(r1=25, r2=15, h=60, anchor=UP+spherical_to_xyz(1,30,90));
 107```
 108
 109---
 110
 111For Spherical type attachables, you can pass a vector that points at any arbitrary place on
 112the surface of the sphere:
 113p
 114```openscad-3D
 115include <BOSL2/std.scad>
 116sphere(r=50, anchor=TOP);
 117```
 118
 119```openscad-3D
 120include <BOSL2/std.scad>
 121sphere(r=50, anchor=TOP+FRONT);
 122```
 123
 124```openscad-3D
 125include <BOSL2/std.scad>
 126sphere(r=50, anchor=spherical_to_xyz(1,-30,60));
 127```
 128
 129---
 130
 131Some attachable shapes may provide specific named anchors for shape-specific anchoring.  These
 132will be given as strings and will be specific to that type of
 133attachable.  When named anchors are supported, they are listed in a
 134"Named Anchors" section of the documentation for the module.  The
 135`teardrop()` attachable, for example, has a named anchor called "cap" and in 2D the
 136`star()` attachable has anchors labeled by tip number: 
 137
 138```openscad-3D
 139include <BOSL2/std.scad>
 140teardrop(d=100, l=20, anchor="cap");
 141```
 142
 143```openscad-2D
 144include <BOSL2/std.scad>
 145star(n=7, od=30, id=20, anchor="tip2");
 146```
 147
 148---
 149
 150Some shapes, for backwards compatibility reasons, can take a `center=` argument.  This just
 151overrides the `anchor=` argument.  A `center=true` argument is the same as `anchor=CENTER`.
 152A `center=false` argument chooses the anchor to match the behavior of
 153the builtin version:  for a cube it is the same as `anchor=[-1,-1,-1]` but for a
 154cylinder, it is the same as `anchor=BOTTOM`.
 155
 156```openscad-3D
 157include <BOSL2/std.scad>
 158cube([50,40,30],center=true);
 159```
 160
 161```openscad-3D
 162include <BOSL2/std.scad>
 163cube([50,40,30],center=false);
 164```
 165
 166---
 167
 168Most 2D shapes provided by BOSL2 are also anchorable.  The built-in `square()` and `circle()`
 169modules have been overridden to make them attachable..  The `anchor=` options for 2D
 170shapes treat 2D vectors as expected.  Special handling occurs with 3D
 171vectors:  if the Y coordinate is zero and the Z coordinate is nonzero,
 172then the Z coordinate is used to replace the Y coordinate.  This is
 173done so that you can use the TOP and BOTTOM names as anchor for 2D
 174shapes.  
 175
 176
 177```openscad-2D
 178include <BOSL2/std.scad>
 179square([40,30], anchor=BACK+LEFT);
 180```
 181
 182```openscad-2D
 183include <BOSL2/std.scad>
 184circle(d=50, anchor=BACK);
 185```
 186
 187```openscad-2D
 188include <BOSL2/std.scad>
 189hexagon(d=50, anchor=LEFT);
 190```
 191
 192```openscad-2D
 193include <BOSL2/std.scad>
 194ellipse(d=[50,30], anchor=FRONT);
 195
 196This final 2D example shows using the 3D anchor, TOP, with a 2D
 197object.  Also notice how the pentagon anchors to its most extreme point on
 198the Y+ axis.  
 199
 200```openscad-2D
 201include <BOSL2/std.scad>
 202pentagon(d=50, anchor=TOP);
 203```
 204
 205
 206## Spin
 207You can spin attachable objects around the origin using the `spin=`
 208argument.  The spin applies **after** anchoring, so depending on how
 209you anchor an object, its spin may not be about its center.  This
 210means that spin can have an effect even on rotationally symmetric
 211objects like spheres and cylinders.  You specify the spin in degrees.
 212A positive number will result in a counter-clockwise spin around the Z
 213axis (as seen from above), and a negative number will make a clockwise
 214spin:
 215
 216```openscad-3D
 217include <BOSL2/std.scad>
 218cube([20,20,40], center=true, spin=45);
 219```
 220
 221You can also spin around other axes, or multiple axes at once, by giving 3 angles (in degrees) to
 222`spin=` as a vector, like [Xang,Yang,Zang].  Similarly to `rotate()`,
 223the rotations apply in the order given, X-axis spin, then Y-axis, then Z-axis:
 224
 225```openscad-3D
 226include <BOSL2/std.scad>
 227cube([20,20,40], center=true, spin=[10,20,30]);
 228```
 229
 230This example shows a cylinder which has been anchored at its FRONT,
 231with a rotated copy in gray.  The rotation is performed around the
 232origin, but the cylinder is off the origin, so the rotation **does**
 233have an effect on the cylinder, even though the cylinder has
 234rotational symmetry.
 235
 236```openscad-3D
 237include <BOSL2/std.scad>
 238cylinder(h=40,d=20,anchor=FRONT+BOT);
 239%cylinder(h=40.2,d=20,anchor=FRONT+BOT,spin=40);
 240```
 241
 242
 243
 244You can also apply spin to 2D shapes from BOSL2, though only by scalar angle:
 245
 246```openscad-2D
 247include <BOSL2/std.scad>
 248square([40,30], spin=30);
 249```
 250
 251```openscad-2D
 252include <BOSL2/std.scad>
 253ellipse(d=[40,30], spin=30);
 254```
 255
 256
 257## Orientation
 258Another way to specify a rotation for an attachable shape, is to pass a 3D vector via the
 259`orient=` argument.  This lets you specify what direction to tilt the top of the shape towards.
 260For example, you can make a cone that is tilted up and to the right like this:
 261
 262```openscad-3D
 263include <BOSL2/std.scad>
 264cylinder(h=100, r1=50, r2=20, orient=UP+RIGHT);
 265```
 266
 267More precisely, the Z direction of the shape is rotated to align with
 268the vector you specify.  Two dimensional attachables, which have no Z vector,
 269do not accept the `orient=` argument.  
 270
 271
 272## Mixing Anchoring, Spin, and Orientation
 273When giving `anchor=`, `spin=`, and `orient=`, they are applied anchoring first, spin second,
 274then orient last.  For example, here's a cube:
 275
 276```openscad-3D
 277include <BOSL2/std.scad>
 278cube([20,20,50]);
 279```
 280
 281You can center it with an `anchor=CENTER` argument:
 282
 283```openscad-3D
 284include <BOSL2/std.scad>
 285cube([20,20,50], anchor=CENTER);
 286```
 287
 288Add a 45 degree spin:
 289
 290```openscad-3D
 291include <BOSL2/std.scad>
 292cube([20,20,50], anchor=CENTER, spin=45);
 293```
 294
 295Now tilt the top up and forward:
 296
 297```openscad-3D
 298include <BOSL2/std.scad>
 299cube([20,20,50], anchor=CENTER, spin=45, orient=UP+FWD);
 300```
 301
 302For 2D shapes, you can mix `anchor=` with `spin=`, but not with `orient=`.
 303
 304```openscad-2D
 305include <BOSL2/std.scad>
 306square([40,30], anchor=BACK+LEFT, spin=30);
 307```
 308
 309## Positioning Children
 310
 311Positioning is a powerful method for placing an object relative to
 312another object.  You do this by making the second object a child of
 313the first object.  By default, the child's anchor point will be
 314aligned with the center of the parent.  The default anchor for `cyl()`
 315is CENTER, and in this case, the cylinder is centered on the cube's center
 316
 317```openscad-3D
 318include <BOSL2/std.scad>
 319up(13) cube(50)
 320    cyl(d=25,l=95);
 321```
 322
 323With `cylinder()` the default anchor is BOTTOM.  It's hard to tell,
 324but the cylinder's bottom is placed at the center of the cube.  
 325
 326```openscad-3D
 327include <BOSL2/std.scad>
 328cube(50)
 329    cylinder(d=25,h=75);
 330```
 331
 332If you explicitly anchor the child object then the anchor you choose will be aligned
 333with the center point of the parent object.  In this example the right
 334side of the cylinder is aligned with the center of the cube.  
 335
 336
 337```openscad-3D
 338include <BOSL2/std.scad>
 339cube(50,anchor=FRONT)     
 340    cylinder(d=25,h=95,anchor=RIGHT);
 341```
 342
 343The `position()` module enables you to specify where on the parent to
 344position the child object.  You give `position()` an anchor point on
 345the parent, and the child's anchor point is aligned with the specified
 346parent anchor point.  In this example the LEFT anchor of the cylinder is positioned on the
 347RIGHT anchor of the cube.  
 348
 349```openscad-3D
 350include <BOSL2/std.scad>
 351cube(50,anchor=FRONT)     
 352    position(RIGHT) cylinder(d=25,h=75,anchor=LEFT);
 353```
 354
 355Using this mechanism you can position objects relative to other
 356objects which are in turn positioned relative to other objects without
 357having to keep track of the transformation math.
 358
 359```openscad-3D
 360include <BOSL2/std.scad>
 361cube([50,50,30],center=true)
 362    position(TOP+RIGHT) cube([25,40,10], anchor=RIGHT+BOT)
 363       position(LEFT+FRONT+TOP) cube([12,12,8], anchor=LEFT+FRONT+BOT)
 364         cylinder(h=10,r=3);
 365```
 366
 367The positioning mechanism is not magical: it simply applies a
 368`translate()` operation to the child.  You can still apply your own
 369additional translations or other transformations if you wish.  For
 370example, you can position an object 5 units from the right edge:
 371
 372```openscad-3D
 373include<BOSL2/std.scad>
 374cube([50,50,20],center=true)
 375    position(TOP+RIGHT) left(5) cube([4,50,10], anchor=RIGHT+BOT);
 376```
 377
 378
 379
 380Positioning objects works the same way in 2D.
 381
 382```openscad-2D
 383include<BOSL2/std.scad>
 384square(10)
 385    position(RIGHT) square(3,anchor=LEFT);
 386```
 387
 388## Using position() with orient()
 389
 390When positioning an object near an edge or corner you may wish to
 391orient the object relative to some face other than the TOP face that
 392meets at that edge or corner.  You can always apply a `rotation()` to 
 393change the orientation of the child object, but in order to do this,
 394you need to figure out the correct rotation.  The `orient()` module provides a
 395mechanism for re-orienting the child() that eases this burden: 
 396it can orient the child relative to the parent anchor directions.  This is different
 397than giving an `orient=` argument to the child, because that orients
 398relative to the parent's global coordinate system by just using the vector
 399directly, instead of orienting to the parent's anchor, which takes
 400account of face orientation.  A series of three
 401examples shows the different results.  In the first example, we use
 402only `position()`.  The child cube is erected pointing upwards, in the
 403Z direction.  In the second example we use `orient=RIGHT` in the child
 404and the result is that the child object points in the X+ direction,
 405without regard for the shape of the parent object.  In the final
 406example we apply `orient(RIGHT)` and the child is oriented
 407relative to the slanted right face of the parent using the parent
 408RIGHT anchor.   
 409
 410```openscad-3D
 411include<BOSL2/std.scad>
 412prismoid([50,50],[30,30],h=40)
 413  position(RIGHT+TOP)
 414     cube([15,15,25],anchor=RIGHT+BOT);
 415```
 416
 417
 418```openscad-3D
 419include<BOSL2/std.scad>
 420prismoid([50,50],[30,30],h=40)
 421  position(RIGHT+TOP)
 422     cube([15,15,25],orient=RIGHT,anchor=LEFT+BOT);
 423```
 424
 425
 426```openscad-3D
 427include<BOSL2/std.scad>
 428prismoid([50,50],[30,30],h=40)
 429  position(RIGHT+TOP)
 430     orient(RIGHT)
 431        cube([15,15,25],anchor=BACK+BOT);
 432```
 433
 434You may have noticed that the children in the above three examples
 435have different anchors.  Why is that?  The first and second examples
 436differ because anchoring up and anchoring to the right require
 437anchoring on opposite sides of the child.  But the third case differs
 438because the spin has changed.  The examples below show the same models
 439but with arrows replacing the child cube.  The red flags on the arrows
 440mark the zero spin direction.  Examine the red flags to see how the spin
 441changes.  The Y+ direction of the child will point towards that red
 442flag.  
 443
 444```openscad-3D
 445include<BOSL2/std.scad>
 446prismoid([50,50],[30,30],h=40)
 447  position(RIGHT+TOP)
 448     anchor_arrow(40);
 449```
 450
 451
 452```openscad-3D
 453include<BOSL2/std.scad>
 454prismoid([50,50],[30,30],h=40)
 455  position(RIGHT+TOP)
 456     anchor_arrow(40, orient=RIGHT);
 457```
 458
 459```openscad-3D
 460include<BOSL2/std.scad>
 461prismoid([50,50],[30,30],h=40)
 462  position(RIGHT+TOP)
 463     orient(RIGHT)
 464        anchor_arrow(40);
 465```
 466
 467
 468## Aligning children with align()
 469
 470You may have noticed that with position() and orient(), specifying the
 471child anchors to position objects flush with their parent can be
 472annoying, or sometimes even tricky.  You can simplify this task by
 473using the align() module.  This module positions children at specified
 474anchor points on the parent while picking the correct anchor points on
 475the children so that they line up with faces on the parent object.
 476
 477In the simplest case, if you want to place a child on the RIGHT side
 478of its parent, you need to anchor the child to its LEFT anchor:
 479
 480```openscad-3D
 481include<BOSL2/std.scad>
 482cuboid([50,40,15])
 483    position(RIGHT)
 484        color("lightblue")cuboid(5,anchor=LEFT);
 485```
 486
 487Using align(), the determination of the anchor is automatic.  Any
 488anchor you do specify is ignored.
 489
 490```openscad-3D
 491include<BOSL2/std.scad>
 492cuboid([50,40,15])
 493    align(RIGHT)
 494        color("lightblue")cuboid(5);
 495```
 496
 497To place the child on top of the parent in the corner you can do use
 498align as shown below instead of specifying the RIGHT+FRONT+BOT anchor
 499with position(): 
 500
 501```openscad-3D
 502include<BOSL2/std.scad>
 503cuboid([50,40,15])
 504    align(RIGHT+FRONT+TOP)
 505        color("lightblue")prismoid([10,5],[7,4],height=4);
 506```
 507
 508Both position() and align() can accept a list of anchor locations and
 509makes several copies of the children, but
 510if you want the children positioned flush, each copy 
 511requires a different anchor, so it is impossible to do this with a
 512single call to position(), but easily done using align():
 513
 514```openscad-3D
 515include<BOSL2/std.scad>
 516cuboid([50,40,15])
 517    align([RIGHT+TOP,LEFT+TOP])
 518        color("lightblue")prismoid([10,5],[7,4],height=4);
 519```
 520
 521Align also accepts a spin argument, which lets you spin the child
 522while still aligning it:
 523
 524```openscad-3D
 525include<BOSL2/std.scad>
 526cuboid([50,40,15])
 527    align(RIGHT+TOP,spin=90)
 528        color("lightblue")prismoid([10,5],[7,4],height=4);
 529```
 530
 531Note that this is different than using the spin argument to the child
 532object, which will apply after alignment has been done.
 533
 534
 535```openscad-3D
 536include<BOSL2/std.scad>
 537cuboid([50,40,15])
 538    align(RIGHT+TOP)
 539        color("lightblue")prismoid([10,5],[7,4],height=4,spin=90);
 540```
 541
 542If you orient the object DOWN it will be attached from its top anchor:
 543
 544```openscad-3D
 545include<BOSL2/std.scad>
 546cuboid([50,40,15])
 547    align(RIGHT+TOP,DOWN)
 548        color("lightblue")prismoid([10,5],[7,4],height=4);
 549```
 550
 551When placing children on the RIGHT and LEFT, there is a spin applied.
 552This means that setting spin=0 changes the orientation.  Here we have
 553one object with the default and one object with zero spin:
 554
 555```openscad-3D
 556include<BOSL2/std.scad>
 557prismoid(50,30,25){
 558  align(RIGHT+TOP,RIGHT,spin=0)
 559    color("lightblue")prismoid([10,5],[7,4],height=4);
 560  align(RIGHT+BOT,RIGHT)
 561    color("green")prismoid([10,5],[7,4],height=4);
 562}
 563```
 564
 565
 566## Attachment overview
 567
 568Attachables get their name from their ability to be attached to each
 569other.  Unlike with positioning, attaching changes the orientation of
 570the child object.  When you attach an object, it appears on the parent
 571relative to the local coordinate system of the parent at the anchor point.  To understand
 572what this means, imagine the perspective of an ant walking on a
 573sphere.  The meaning of UP varies depending on where on the sphere the
 574ant is standing.  If you **attach** a cylinder to the sphere then the cylinder will
 575be "up" from the ant's perspective.   The first example shows a
 576cylinder placed with `position()` so it points up in the global parent
 577coordinate system.  The second example shows how `attach()` points the
 578cylinder UP from the perspective of an ant standing at the anchor
 579point on the sphere.  
 580
 581```openscad-3D
 582include<BOSL2/std.scad>
 583sphere(40)
 584    position(RIGHT+TOP) cylinder(r=8,h=20);
 585```
 586
 587
 588```openscad-3D
 589include<BOSL2/std.scad>
 590sphere(40)
 591    attach(RIGHT+TOP) cylinder(r=8,h=20);
 592```
 593
 594In the example above, the cylinder's center point is attached to the
 595sphere, pointing "up" from the perspective of the sphere's surface.
 596For a sphere, a surface normal is defined everywhere that specifies
 597what "up" means.  But for other objects, it may not be so obvious.
 598Usually at edges and corners the direction is the average of the
 599direction of the faces that meet there.
 600
 601When you specify an anchor you are actually specifying both an anchor
 602point but also an anchor direction.  If you want to visualize this
 603direction you can use anchor arrows.  
 604
 605
 606## Anchor Directions and Anchor Arrows
 607For the ant on the sphere it is obvious which direction is UP; that
 608direction corresponds to the Z+ axis.  The location of the X and Y
 609axes is less clear and in fact it may be arbitrary.  One way that is
 610useful to show the position and orientation of an anchor point is by
 611attaching an anchor arrow to that anchor.  As noted before, the small
 612red flag points in the direction of the anchor's Y+ axis when the spin
 613is zero.
 614
 615```openscad-3D
 616include <BOSL2/std.scad>
 617cube(18, center=true)
 618    attach(LEFT+TOP)
 619        anchor_arrow();
 620```
 621
 622For large objects, you can change the size of the arrow with the `s=` argument.
 623
 624```openscad-3D
 625include <BOSL2/std.scad>
 626sphere(d=100)
 627    attach(LEFT+TOP)
 628        anchor_arrow(s=50);
 629```
 630
 631To show all the standard cardinal anchor points, you can use the `show_anchors()` module.
 632
 633```openscad-3D
 634include <BOSL2/std.scad>
 635cube(40, center=true)
 636    show_anchors();
 637```
 638
 639```openscad-3D
 640include <BOSL2/std.scad>
 641cylinder(h=40, d=40, center=true)
 642    show_anchors();
 643```
 644
 645```openscad-3D
 646include <BOSL2/std.scad>
 647sphere(d=40)
 648    show_anchors();
 649```
 650
 651For large objects, you can again change the size of the arrows with the `s=` argument.
 652
 653```openscad-3D
 654include <BOSL2/std.scad>
 655cylinder(h=100, d=100, center=true)
 656    show_anchors(s=30);
 657```
 658
 659## Basic Attachment
 660
 661The simplest form of attachment is to attach using the `attach()`
 662module with a single argument, which specifies the anchor on the parent
 663where the child will attach.  This will attach the bottom of the child
 664to the given anchor point on the parent.  The child appears on the parent with its
 665Z direction aligned parallel to the parent's anchor direction, and
 666its Y direction pointing in the zero spin direction for the
 667parent anchor.  The anchor direction of the child does not affect the result in this
 668case.
 669
 670```openscad-3D
 671include <BOSL2/std.scad>
 672cube(50,center=true)
 673    attach(RIGHT)cylinder(d1=30,d2=15,h=25);
 674```
 675
 676```openscad-3D
 677include <BOSL2/std.scad>
 678cube(50,center=true)
 679    attach(RIGHT+TOP)cylinder(d1=30,d2=15,h=25);
 680```
 681
 682In the second example, the child object points diagonally away
 683from the cube.  If you want the child at at edge of the parent it's
 684likely that this result will not be what you want.  To get a different
 685result, use `position()` with `orient()`, if needed. 
 686
 687If you give an anchor point to the child object it moves the child
 688around (in the attached coordinate system).  Or alternatively you can
 689think that it moves the object first, and then it gets attached.
 690
 691```openscad-3D
 692include <BOSL2/std.scad>
 693cube(50,center=true)
 694    attach(RIGHT)cylinder(d1=30,d2=15,h=25,anchor=FRONT);
 695```
 696
 697In the above example we anchor the child to its FRONT and then attach
 698it to the RIGHT.  An ambiguity exists regarding the spin of the
 699parent's coordinate system.  How is this resolved?   The small flags
 700on the anchor arrows show the position of zero spin by pointing
 701towards the local Y+ direction, which is also the BACK direction of the child.  For the above
 702cube, the arrow looks like this:
 703
 704```openscad-3D
 705include <BOSL2/std.scad>
 706cube(50,center=true)
 707    attach(RIGHT)anchor_arrow(30);
 708```
 709
 710The red flag points up, which explains why the attached cylinder
 711appeared above the anchor point.  The CENTER anchor generally has a
 712direction that points upward, so an attached object will keep its
 713orientation if attached to the CENTER of a parent.
 714
 715By default, `attach()` places the child exactly flush with the surface of the parent.  Sometimes
 716it's useful to have the child overlap the parent by insetting a bit.  You can do this with the
 717`overlap=` argument to `attach()`.  A positive value will inset the child into the parent, and
 718a negative value will outset out from the parent, which may be helpful
 719when doing differences.  
 720
 721```openscad-3D
 722include <BOSL2/std.scad>
 723cube(50,center=true)
 724    attach(TOP,overlap=10)
 725        cylinder(d=20,h=20);
 726```
 727
 728```openscad-3D
 729include <BOSL2/std.scad>
 730cube(50,center=true)
 731    attach(TOP,overlap=-20)
 732        cylinder(d=20,h=20);
 733```
 734
 735As with `position()`, you can still apply your own translations and
 736other transformations even after attaching an object.  However, the
 737order of operations now matters.  If you apply a translation outside
 738of the anchor then it acts in the parent's global coordinate system, so the
 739child moves up in this example:
 740
 741```openscad-3D
 742include <BOSL2/std.scad>
 743cube(50,center=true)
 744    up(13)
 745        attach(RIGHT)
 746            cylinder(d1=30,d2=15,h=25);
 747```
 748
 749On the other hand, if you put the translation between the attach and
 750the object in your code, then it will act in the local coordinate system of
 751the parent at the parent's anchor, so in the example below it moves to the right.  
 752
 753```openscad-3D
 754include <BOSL2/std.scad>
 755cube(50,center=true)
 756    attach(RIGHT)
 757        up(13)
 758            cylinder(d1=30,d2=15,h=25);
 759```
 760
 761
 762## Attachment With Parent and Child Anchors
 763
 764The `attach()` module can also take a second argument, the child anchor.
 765In this case, the attachment behavior
 766is quite different.  The objects are still attached with their anchor
 767points aligned, but the child is reoriented so that its anchor
 768direction is the opposite of the parent anchor direction.  It's like
 769you assemble the parts by pushing them together in the direction of
 770their anchor arrows.  Two examples appear below, where first we show
 771two objects with their anchors and then we show the result of
 772attaching with those anchors. 
 773
 774```openscad-3D
 775include <BOSL2/std.scad>
 776cube(50,anchor=BOT) attach(TOP) anchor_arrow(30);
 777right(60)cylinder(d1=30,d2=15,h=25) attach(TOP) anchor_arrow(30);
 778```
 779
 780```openscad-3D
 781include <BOSL2/std.scad>
 782cube(50,anchor=BOT)
 783  attach(TOP,TOP) cylinder(d1=30,d2=15,h=25);
 784```
 785
 786```openscad-3D
 787include <BOSL2/std.scad>
 788cube(50,center=true) attach(RIGHT) anchor_arrow(30);
 789right(80)cylinder(d1=30,d2=15,h=25) attach(LEFT) anchor_arrow(30);
 790```
 791
 792```openscad-3D
 793include <BOSL2/std.scad>
 794cube(50,center=true)
 795  attach(RIGHT,LEFT) cylinder(d1=30,d2=15,h=25);
 796```
 797
 798Note that when you attach with two anchors like this, the attachment
 799operation **overrides any anchor or orientation specified in the
 800child**.  That means the child's `anchor=` and `orient=` options are
 801ignored.
 802
 803Attachment with CENTER anchors can be surprising because the anchors
 804point upwards, so in the example below, the child's CENTER anchor
 805points up, so it is inverted when it is attached to the parent cone.
 806Note that the anchors are CENTER anchors, so the bases of the anchors are
 807hidden in the middle of the objects. 
 808
 809```openscad-3D
 810include <BOSL2/std.scad>
 811cylinder(d1=30,d2=15,h=25) attach(CENTER) anchor_arrow(40);
 812right(40)cylinder(d1=30,d2=15,h=25) attach(CENTER) anchor_arrow(40);
 813```
 814
 815```openscad-3D
 816include <BOSL2/std.scad>
 817cylinder(d1=30,d2=15,h=25)
 818    attach(CENTER,CENTER)
 819        cylinder(d1=30,d2=15,h=25);
 820```
 821
 822
 823## Positioning and Attaching Multiple Children
 824
 825You can attach or position more than one child at a time by enclosing them all in braces:
 826
 827```openscad-3D
 828include <BOSL2/std.scad>
 829cube(50, center=true) {
 830    attach(TOP) cylinder(d1=50,d2=20,h=20);
 831    position(RIGHT) cylinder(d1=50,d2=20,h=20);
 832}
 833```
 834
 835If you want to attach the same shape to multiple places on the same parent, you can pass the
 836desired anchors as a list to the `attach()` or `position()` modules:
 837
 838```openscad-3D
 839include <BOSL2/std.scad>
 840cube(50, center=true)
 841    attach([RIGHT,FRONT],TOP) cylinder(d1=35,d2=20,h=25);
 842```
 843
 844```openscad-3D
 845include <BOSL2/std.scad>
 846cube(50, center=true)
 847    position([TOP,RIGHT,FRONT]) cylinder(d1=35,d2=20,h=25);
 848```
 849
 850
 851## Attaching 2D Children
 852You can use attachments in 2D as well.  As usual for the 2D case you
 853can use TOP and BOTTOM as alternative to BACK and FORWARD.  
 854
 855```openscad-2D
 856include <BOSL2/std.scad>
 857square(50,center=true)
 858    attach(RIGHT,FRONT)
 859        trapezoid(w1=30,w2=0,h=30);
 860```
 861
 862```openscad-2D
 863include <BOSL2/std.scad>
 864circle(d=50)
 865    attach(TOP,BOT,overlap=5)
 866        trapezoid(w1=30,w2=0,h=30);
 867```
 868
 869
 870
 871
 872## Tagged Operations
 873BOSL2 introduces the concept of tags.  Tags are names that can be given to attachables, so that
 874you can refer to them when performing `diff()`, `intersect()`, and `conv_hull()` operations.
 875Each object can have no more than one tag at a time.  
 876
 877### `diff([remove], [keep])`
 878The `diff()` operator is used to difference away all shapes marked with the tag(s) given to
 879`remove`, from the other shapes.  
 880
 881For example, to difference away a child cylinder from the middle of a parent cube, you can
 882do this:
 883
 884```openscad-3D
 885include <BOSL2/std.scad>
 886diff("hole")
 887cube(100, center=true)
 888    tag("hole")cylinder(h=101, d=50, center=true);
 889```
 890
 891The `keep=` argument takes tags for shapes that you want to keep in the output.
 892
 893```openscad-3D
 894include <BOSL2/std.scad>
 895diff("dish", keep="antenna")
 896cube(100, center=true)
 897    attach([FRONT,TOP], overlap=33) {
 898        tag("dish") cylinder(h=33.1, d1=0, d2=95);
 899        tag("antenna") cylinder(h=33.1, d=10);
 900    }
 901```
 902
 903Remember that tags are inherited by children.  In this case, we need to explicitly
 904untag the first cylinder (or change its tag to something else), or it
 905will inherit the "keep" tag and get kept.  
 906
 907```openscad-3D
 908include <BOSL2/std.scad>
 909diff("hole", "keep")
 910tag("keep")cube(100, center=true)
 911    attach([RIGHT,TOP]) {
 912        tag("") cylinder(d=95, h=5);
 913        tag("hole") cylinder(d=50, h=11, anchor=CTR);
 914    }
 915```
 916
 917
 918You can of course apply `tag()` to several children.
 919
 920```openscad-3D
 921include <BOSL2/std.scad>
 922diff("hole")
 923cube(100, center=true)
 924    attach([FRONT,TOP], overlap=20)
 925        tag("hole") {
 926            cylinder(h=20.1, d1=0, d2=95);
 927            down(10) cylinder(h=30, d=30);
 928        }
 929```
 930
 931Many of the modules that use tags have default values for their tags.  For diff the default
 932remove tag is "remove" and the default keep tag is "keep".  In this example we rely on the
 933default values:
 934
 935```openscad-3D
 936include <BOSL2/std.scad>
 937diff()
 938sphere(d=100) {
 939    tag("keep")xcyl(d=40, l=120);
 940    tag("remove")cuboid([40,120,100]);
 941}
 942```
 943
 944
 945The parent object can be differenced away from other shapes.  Tags are inherited by children,
 946though, so you will need to set the tags of the children as well as the parent.
 947
 948```openscad-3D
 949include <BOSL2/std.scad>
 950diff("hole")
 951tag("hole")cube([20,11,45], center=true)
 952    tag("body")cube([40,10,90], center=true);
 953```
 954
 955Tags (and therefore tag-based operations like `diff()`) only work correctly with attachable
 956children.  However, a number of built-in modules for making shapes are *not* attachable.
 957Some notable non-attachable modules are `text()`, `linear_extrude()`, `rotate_extrude()`,
 958`polygon()`, `polyhedron()`, `import()`, `surface()`, `union()`, `difference()`,
 959`intersection()`, `offset()`, `hull()`, and `minkowski()`.
 960
 961To allow you to use tags-based operations with non-attachable shapes, you can wrap them with the
 962`force_tag()` module to specify their tags.  For example:
 963
 964```openscad-3D
 965include <BOSL2/std.scad>
 966diff("hole")
 967cuboid(50)
 968  attach(TOP)
 969    force_tag("hole")
 970      rotate_extrude()
 971        right(15)
 972          square(10,center=true);
 973```
 974
 975### `intersect([intersect], [keep])`
 976
 977To perform an intersection of attachables, you can use the `intersect()` module.  This is
 978specifically intended to address the situation where you want intersections involving a parent
 979and a child, something that is impossible with the native `intersection()` module.  This module
 980treats the children in three groups: objects matching the `intersect` tags, objects matching
 981the tags listed in `keep` and the remaining objects that don't match any listed tags.  The
 982intersection is computed between the union of the `intersect` tagged objects and the union of
 983the objects that don't match any listed tags.  Finally the objects listed in `keep` are union
 984ed with the result.  
 985
 986In this example the parent is intersected with a conical bounding shape.  
 987
 988```openscad-3D
 989include <BOSL2/std.scad>
 990intersect("bounds")
 991cube(100, center=true)
 992    tag("bounds") cylinder(h=100, d1=120, d2=95, center=true, $fn=72);
 993```
 994
 995In this example the child objects are intersected with the bounding box parent.  
 996
 997```openscad-3D
 998include <BOSL2/std.scad>
 999intersect("pole cap")
1000cube(100, center=true)
1001    attach([TOP,RIGHT]) {
1002        tag("pole")cube([40,40,80],center=true);
1003        tag("cap")sphere(d=40*sqrt(2));
1004    }
1005```
1006
1007The default `intersect` tag is "intersect" and the default `keep` tag is "keep".  Here is an
1008example where "keep" is used to keep the pole from being removed by the intersection. 
1009
1010```openscad-3D
1011include <BOSL2/std.scad>
1012intersect()
1013cube(100, center=true) {
1014    tag("intersect")cylinder(h=100, d1=120, d2=95, center=true, $fn=72);
1015    tag("keep")zrot(45) xcyl(h=140, d=20, $fn=36);
1016}
1017```
1018
1019### `conv_hull([keep])`
1020You can use the `conv_hull()` module to hull shapes together.  Objects
1021marked with the keep tags are excluded from the hull and unioned into the final result.
1022The default keep tag is "keep".  
1023
1024
1025```openscad-3D
1026include <BOSL2/std.scad>
1027conv_hull()
1028cube(50, center=true) {
1029    cyl(h=100, d=20);
1030    tag("keep")xcyl(h=100, d=20);
1031}
1032```
1033
1034
1035## 3D Masking Attachments
1036To make it easier to mask away shapes from various edges of an attachable parent shape, there
1037are a few specialized alternatives to the `attach()` and `position()` modules.
1038
1039### `edge_mask()`
1040If you have a 3D mask shape that you want to difference away from various edges, you can use
1041the `edge_mask()` module.  This module will take a vertically oriented shape, and will rotate
1042and move it such that the BACK, RIGHT (X+,Y+) side of the shape will be aligned with the given
1043edges.  The shape will be tagged as a "remove" so that you can use
1044`diff()` with its default "remove" tag.  For example,
1045here's a shape for rounding an edge:
1046
1047```openscad-3D
1048include <BOSL2/std.scad>
1049module round_edge(l,r) difference() {
1050    translate([-1,-1,-l/2])
1051        cube([r+1,r+1,l]);
1052    translate([r,r])
1053        cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4));
1054}
1055round_edge(l=30, r=19);
1056```
1057
1058You can use that mask to round various edges of a cube:
1059
1060```openscad-3D
1061include <BOSL2/std.scad>
1062module round_edge(l,r) difference() {
1063    translate([-1,-1,-l/2])
1064        cube([r+1,r+1,l]);
1065    translate([r,r])
1066        cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4));
1067}
1068diff()
1069cube([50,60,70],center=true)
1070    edge_mask([TOP,"Z"],except=[BACK,TOP+LEFT])
1071        round_edge(l=71,r=10);
1072```
1073
1074### `corner_mask()`
1075If you have a 3D mask shape that you want to difference away from various corners, you can use
1076the `corner_mask()` module.  This module will take a shape and rotate and move it such that the
1077BACK RIGHT TOP (X+,Y+,Z+) side of the shape will be aligned with the given corner.  The shape
1078will be tagged as a "remove" so that you can use `diff()` with its
1079default "remove" tag.  For example, here's a shape for
1080rounding a corner:
1081
1082```openscad-3D
1083include <BOSL2/std.scad>
1084module round_corner(r) difference() {
1085    translate(-[1,1,1])
1086        cube(r+1);
1087    translate([r,r,r])
1088        spheroid(r=r, style="aligned", $fn=quantup(segs(r),4));
1089}
1090round_corner(r=10);
1091```
1092
1093You can use that mask to round various corners of a cube:
1094
1095```openscad-3D
1096include <BOSL2/std.scad>
1097module round_corner(r) difference() {
1098    translate(-[1,1,1])
1099        cube(r+1);
1100    translate([r,r,r])
1101        spheroid(r=r, style="aligned", $fn=quantup(segs(r),4));
1102}
1103diff()
1104cube([50,60,70],center=true)
1105    corner_mask([TOP,FRONT],LEFT+FRONT+TOP)
1106        round_corner(r=10);
1107```
1108
1109### Mix and Match Masks
1110You can use `edge_mask()` and `corner_mask()` together as well:
1111
1112```openscad-3D
1113include <BOSL2/std.scad>
1114module round_corner(r) difference() {
1115    translate(-[1,1,1])
1116        cube(r+1);
1117    translate([r,r,r])
1118        spheroid(r=r, style="aligned", $fn=quantup(segs(r),4));
1119}
1120module round_edge(l,r) difference() {
1121    translate([-1,-1,-l/2])
1122        cube([r+1,r+1,l]);
1123    translate([r,r])
1124        cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4));
1125}
1126diff()
1127cube([50,60,70],center=true) {
1128    edge_mask("ALL") round_edge(l=71,r=10);
1129    corner_mask("ALL") round_corner(r=10);
1130}
1131```
1132
1133## 2D Profile Mask Attachments
1134While 3D mask shapes give you a great deal of control, you need to make sure they are correctly
1135sized, and you need to provide separate mask shapes for corners and edges.  Often, a single 2D
1136profile could be used to describe the edge mask shape (via `linear_extrude()`), and the corner
1137mask shape (via `rotate_extrude()`).  This is where `edge_profile()`, `corner_profile()`, and
1138`face_profile()` come in.
1139
1140### `edge_profile()`
1141Using the `edge_profile()` module, you can provide a 2D profile shape and it will be linearly
1142extruded to a mask of the appropriate length for each given edge.  The resultant mask will be
1143tagged with "remove" so that you can difference it away with `diff()`
1144with the default "remove" tag.  The 2D profile is
1145assumed to be oriented with the BACK, RIGHT (X+,Y+) quadrant as the "cutter edge" that gets
1146re-oriented towards the edges of the parent shape.  A typical mask profile for chamfering an
1147edge may look like:
1148
1149```openscad-2D
1150include <BOSL2/std.scad>
1151mask2d_roundover(10);
1152```
1153
1154Using that mask profile, you can mask the edges of a cube like:
1155
1156```openscad-3D
1157include <BOSL2/std.scad>
1158diff()
1159cube([50,60,70],center=true)
1160   edge_profile("ALL")
1161       mask2d_roundover(10);
1162```
1163
1164### `corner_profile()`
1165You can use the same profile to make a rounded corner mask as well:
1166
1167```openscad-3D
1168include <BOSL2/std.scad>
1169diff()
1170cube([50,60,70],center=true)
1171   corner_profile("ALL", r=10)
1172       mask2d_roundover(10);
1173```
1174
1175### `face_profile()`
1176As a simple shortcut to apply a profile mask to all edges and corners of a face, you can use the
1177`face_profile()` module:
1178
1179```openscad-3D
1180include <BOSL2/std.scad>
1181diff()
1182cube([50,60,70],center=true)
1183   face_profile(TOP, r=10)
1184       mask2d_roundover(10);
1185```
1186
1187
1188## Coloring Attachables
1189Usually, when coloring a shape with the `color()` module, the parent color overrides the colors of
1190all children.  This is often not what you want:
1191
1192```openscad-3D
1193include <BOSL2/std.scad>
1194$fn = 24;
1195color("red") spheroid(d=3) {
1196    attach(CENTER,BOT) color("white") cyl(h=10, d=1) {
1197        attach(TOP,BOT) color("green") cyl(h=5, d1=3, d2=0);
1198    }
1199}
1200```
1201
1202If you use the `recolor()` module, however, the child's color
1203overrides the color of the parent.  This is probably easier to understand by example:
1204
1205```openscad-3D
1206include <BOSL2/std.scad>
1207$fn = 24;
1208recolor("red") spheroid(d=3) {
1209    attach(CENTER,BOT) recolor("white") cyl(h=10, d=1) {
1210        attach(TOP,BOT) recolor("green") cyl(h=5, d1=3, d2=0);
1211    }
1212}
1213```
1214
1215Be aware that `recolor()` will only work if you avoid using the native
1216`color()` module.  Also note that `recolor()` still affects all its
1217children.  If you want to color an object without affecting the
1218children you can use `color_this()`.  See the difference below:
1219
1220```openscad-3D
1221include <BOSL2/std.scad>
1222$fn = 24;
1223recolor("red") spheroid(d=3) {
1224    attach(CENTER,BOT) recolor("white") cyl(h=10, d=1) {
1225        attach(TOP,BOT)  cyl(h=5, d1=3, d2=0);
1226    }
1227}
1228right(5)
1229recolor("red") spheroid(d=3) {
1230    attach(CENTER,BOT) color_this("white") cyl(h=10, d=1) {
1231        attach(TOP,BOT)  cyl(h=5, d1=3, d2=0);
1232    }
1233}
1234```
1235
1236As with all of the attachable features, these color modules only work
1237on attachable objects, so they will have no effect on objects you
1238create using `linear_extrude()` or `rotate_extrude()`.  
1239
1240
1241## Making Attachables
1242To make a shape attachable, you just need to wrap it with an `attachable()` module with a
1243basic description of the shape's geometry.  By default, the shape is expected to be centered
1244at the origin.  The `attachable()` module expects exactly two children.  The first will be
1245the shape to make attachable, and the second will be `children()`,
1246literally.
1247
1248### Pass-through Attachables
1249The simplest way to make your own attachable module is to simply pass
1250through to a pre-existing attachable submodule.  This could be
1251appropriate if you want to rename a module, or if the anchors of an
1252existing module are suited to (or good enough for) your object.  In
1253order for your attachable module to work properly you need to accept
1254the `anchor`, `spin` and `orient` parameters, give them suitable
1255defaults, and pass them to the attachable submodule.  Don't forget to
1256pass the children to the attachable submodule as well, or your new
1257module will ignore its children.  
1258
1259```openscad-3D
1260include <BOSL2/std.scad>
1261$fn=32;
1262module cutcube(anchor=CENTER,spin=0,orient=UP)
1263{
1264   tag_scope(){
1265     diff()
1266       cuboid(15, rounding=2, anchor=anchor,spin=spin,orient=orient){
1267         tag("remove")attach(TOP)cuboid(5);
1268         children();
1269       }
1270   }
1271}
1272diff()
1273cutcube()
1274  tag("remove")attach(RIGHT) cyl(d=2,h=8);
1275```
1276
1277### Prismoidal/Cuboidal Attachables
1278To make a cuboidal or prismoidal shape attachable, you use the `size`, `size2`, and `offset`
1279arguments of `attachable()`.
1280
1281In the most basic form, where the shape is fully cuboid, with top and bottom of the same size,
1282and directly over one another, you can just use `size=`.
1283
1284```openscad-3D
1285include <BOSL2/std.scad>
1286module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
1287    attachable(anchor,spin,orient, size=[s*3,s,s]) {
1288        union() {
1289            xcopies(2*s) cube(s, center=true);
1290            xcyl(h=2*s, d=s/4);
1291        }
1292        children();
1293    }
1294}
1295cubic_barbell(100) show_anchors(60);
1296```
1297
1298When the shape is prismoidal, where the top is a different size from the bottom, you can use
1299the `size2=` argument as well. While `size=` takes all three axes sizes, the `size2=` argument
1300only takes the [X,Y] sizes of the top of the shape.
1301
1302```openscad-3D
1303include <BOSL2/std.scad>
1304module prismoidal(size=[100,100,100], scale=0.5, anchor=CENTER, spin=0, orient=UP) {
1305    attachable(anchor,spin,orient, size=size, size2=[size.x, size.y]*scale) {
1306        hull() {
1307            up(size.z/2-0.005)
1308                linear_extrude(height=0.01, center=true)
1309                    square([size.x,size.y]*scale, center=true);
1310            down(size.z/2-0.005)
1311                linear_extrude(height=0.01, center=true)
1312                    square([size.x,size.y], center=true);
1313        }
1314        children();
1315    }
1316}
1317prismoidal([100,60,30], scale=0.5) show_anchors(20);
1318```
1319
1320When the top of the prismoid can be shifted away from directly above the bottom, you can use
1321the `shift=` argument.  The `shift=` argument takes an [X,Y] vector of the offset of the center
1322of the top from the XY center of the bottom of the shape.
1323
1324```openscad-3D
1325include <BOSL2/std.scad>
1326module prismoidal(size=[100,100,100], scale=0.5, shift=[0,0], anchor=CENTER, spin=0, orient=UP) {
1327    attachable(anchor,spin,orient, size=size, size2=[size.x, size.y]*scale, shift=shift) {
1328        hull() {
1329            translate([shift.x, shift.y, size.z/2-0.005])
1330                linear_extrude(height=0.01, center=true)
1331                    square([size.x,size.y]*scale, center=true);
1332            down(size.z/2-0.005)
1333                linear_extrude(height=0.01, center=true)
1334                    square([size.x,size.y], center=true);
1335        }
1336        children();
1337    }
1338}
1339prismoidal([100,60,30], scale=0.5, shift=[-30,20]) show_anchors(20);
1340```
1341
1342In the case that the prismoid is not oriented vertically, (ie, where the `shift=` or `size2=`
1343arguments should refer to a plane other than XY) you can use the `axis=` argument.  This lets
1344you make prismoids naturally oriented forwards/backwards or sideways.
1345
1346```openscad-3D
1347include <BOSL2/std.scad>
1348module yprismoidal(
1349    size=[100,100,100], scale=0.5, shift=[0,0],
1350    anchor=CENTER, spin=0, orient=UP
1351) {
1352    attachable(
1353        anchor, spin, orient,
1354        size=size, size2=point2d(size)*scale,
1355        shift=shift, axis=BACK
1356    ) {
1357        xrot(-90) hull() {
1358            translate([shift.x, shift.y, size.z/2-0.005])
1359                linear_extrude(height=0.01, center=true)
1360                    square([size.x,size.y]*scale, center=true);
1361            down(size.z/2-0.005)
1362                linear_extrude(height=0.01, center=true)
1363                    square([size.x,size.y], center=true);
1364        }
1365        children();
1366    }
1367}
1368yprismoidal([100,60,30], scale=1.5, shift=[20,20]) show_anchors(20);
1369```
1370
1371
1372### Cylindrical Attachables
1373To make a cylindrical shape attachable, you use the `l`, and `r`/`d`, args of `attachable()`.
1374
1375```openscad-3D
1376include <BOSL2/std.scad>
1377module twistar(l,r,d, anchor=CENTER, spin=0, orient=UP) {
1378    r = get_radius(r=r,d=d,dflt=1);
1379    attachable(anchor,spin,orient, r=r, l=l) {
1380        linear_extrude(height=l, twist=90, slices=20, center=true, convexity=4)
1381            star(n=20, r=r, ir=r*0.9);
1382        children();
1383    }
1384}
1385twistar(l=100, r=40) show_anchors(20);
1386```
1387
1388If the cylinder is elipsoidal in shape, you can pass the unequal X/Y sizes as a 2-item vector
1389to the `r=` or `d=` argument.
1390
1391```openscad-3D
1392include <BOSL2/std.scad>
1393module ovalstar(l,rx,ry, anchor=CENTER, spin=0, orient=UP) {
1394    attachable(anchor,spin,orient, r=[rx,ry], l=l) {
1395        linear_extrude(height=l, center=true, convexity=4)
1396            scale([1,ry/rx,1])
1397                star(n=20, r=rx, ir=rx*0.9);
1398        children();
1399    }
1400}
1401ovalstar(l=100, rx=50, ry=30) show_anchors(20);
1402```
1403
1404For cylindrical shapes that aren't oriented vertically, use the `axis=` argument.
1405
1406```openscad-3D
1407include <BOSL2/std.scad>
1408module ytwistar(l,r,d, anchor=CENTER, spin=0, orient=UP) {
1409    r = get_radius(r=r,d=d,dflt=1);
1410    attachable(anchor,spin,orient, r=r, l=l, axis=BACK) {
1411        xrot(-90)
1412            linear_extrude(height=l, twist=90, slices=20, center=true, convexity=4)
1413                star(n=20, r=r, ir=r*0.9);
1414        children();
1415    }
1416}
1417ytwistar(l=100, r=40) show_anchors(20);
1418```
1419
1420### Conical Attachables
1421To make a conical shape attachable, you use the `l`, `r1`/`d1`, and `r2`/`d2`, args of
1422`attachable()`.
1423
1424```openscad-3D
1425include <BOSL2/std.scad>
1426module twistar(l, r,r1,r2, d,d1,d2, anchor=CENTER, spin=0, orient=UP) {
1427    r1 = get_radius(r1=r1,r=r,d1=d1,d=d,dflt=1);
1428    r2 = get_radius(r1=r2,r=r,d1=d2,d=d,dflt=1);
1429    attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) {
1430        linear_extrude(height=l, twist=90, scale=r2/r1, slices=20, center=true, convexity=4)
1431            star(n=20, r=r1, ir=r1*0.9);
1432        children();
1433    }
1434}
1435twistar(l=100, r1=40, r2=20) show_anchors(20);
1436```
1437
1438If the cone is ellipsoidal in shape, you can pass the unequal X/Y sizes as a 2-item vectors
1439to the `r1=`/`r2=` or `d1=`/`d2=` arguments.
1440
1441```openscad-3D
1442include <BOSL2/std.scad>
1443module ovalish(l,rx1,ry1,rx2,ry2, anchor=CENTER, spin=0, orient=UP) {
1444    attachable(anchor,spin,orient, r1=[rx1,ry1], r2=[rx2,ry2], l=l) {
1445        hull() {
1446            up(l/2-0.005)
1447                linear_extrude(height=0.01, center=true)
1448                    ellipse([rx2,ry2]);
1449            down(l/2-0.005)
1450                linear_extrude(height=0.01, center=true)
1451                    ellipse([rx1,ry1]);
1452        }
1453        children();
1454    }
1455}
1456ovalish(l=100, rx1=50, ry1=30, rx2=30, ry2=50) show_anchors(20);
1457```
1458
1459For conical shapes that are not oriented vertically, use the `axis=` argument to indicate the
1460direction of the primary shape axis:
1461
1462```openscad-3D
1463include <BOSL2/std.scad>
1464module ytwistar(l, r,r1,r2, d,d1,d2, anchor=CENTER, spin=0, orient=UP) {
1465    r1 = get_radius(r1=r1,r=r,d1=d1,d=d,dflt=1);
1466    r2 = get_radius(r1=r2,r=r,d1=d2,d=d,dflt=1);
1467    attachable(anchor,spin,orient, r1=r1, r2=r2, l=l, axis=BACK) {
1468        xrot(-90)
1469            linear_extrude(height=l, twist=90, scale=r2/r1, slices=20, center=true, convexity=4)
1470                star(n=20, r=r1, ir=r1*0.9);
1471        children();
1472    }
1473}
1474ytwistar(l=100, r1=40, r2=20) show_anchors(20);
1475```
1476
1477### Spherical Attachables
1478To make a spherical shape attachable, you use the `r`/`d` args of `attachable()`.
1479
1480```openscad-3D
1481include <BOSL2/std.scad>
1482module spikeball(r, d, anchor=CENTER, spin=0, orient=UP) {
1483    r = get_radius(r=r,d=d,dflt=1);
1484    attachable(anchor,spin,orient, r=r*1.1) {
1485        union() {
1486            sphere_copies(r=r, n=512, cone_ang=180) cylinder(r1=r/10, r2=0, h=r/10);
1487            sphere(r=r);
1488        }
1489        children();
1490    }
1491}
1492spikeball(r=50) show_anchors(20);
1493```
1494
1495If the shape is an ellipsoid, you can pass a 3-item vector of sizes to `r=` or `d=`.
1496
1497```openscad-3D
1498include <BOSL2/std.scad>
1499module spikeball(r, d, scale, anchor=CENTER, spin=0, orient=UP) {
1500    r = get_radius(r=r,d=d,dflt=1);
1501    attachable(anchor,spin,orient, r=r*1.1*scale) {
1502        union() {
1503            sphere_copies(r=r, n=512, scale=scale, cone_ang=180) cylinder(r1=r/10, r2=0, h=r/10);
1504            scale(scale) sphere(r=r);
1505        }
1506        children();
1507    }
1508}
1509spikeball(r=50, scale=[0.75,1,1.5]) show_anchors(20);
1510```
1511
1512### VNF Attachables
1513If the shape just doesn't fit into any of the above categories, and you constructed it as a
1514[VNF](vnf.scad), you can use the VNF itself to describe the geometry with the `vnf=` argument.
1515
1516There are two variations to how anchoring can work for VNFs. When `extent=true`, (the default)
1517then a plane is projected out from the origin, perpendicularly in the direction of the anchor,
1518to the furthest distance that intersects with the VNF shape.  The anchor point is then the
1519center of the points that still intersect that plane.
1520
1521```openscad-FlatSpin,VPD=500
1522include <BOSL2/std.scad>
1523module stellate_cube(s=100, anchor=CENTER, spin=0, orient=UP) {
1524    s2 = 3 * s;
1525    verts = [
1526        [0,0,-s2*sqrt(2)/2],
1527        each down(s/2, p=path3d(square(s,center=true))),
1528        each zrot(45, p=path3d(square(s2,center=true))),
1529        each up(s/2, p=path3d(square(s,center=true))),
1530        [0,0,s2*sqrt(2)/2]
1531    ];
1532    faces = [
1533        [0,2,1], [0,3,2], [0,4,3], [0,1,4],
1534        [1,2,6], [1,6,9], [6,10,9], [2,10,6],
1535        [1,5,4], [1,9,5], [9,12,5], [5,12,4],
1536        [4,8,3], [4,12,8], [12,11,8], [11,3,8],
1537        [2,3,7], [3,11,7], [7,11,10], [2,7,10],
1538        [9,10,13], [10,11,13], [11,12,13], [12,9,13]
1539    ];
1540    vnf = [verts, faces];
1541    attachable(anchor,spin,orient, vnf=vnf) {
1542        vnf_polyhedron(vnf);
1543        children();
1544    }
1545}
1546stellate_cube(25) {
1547    attach(UP+RIGHT) {
1548        anchor_arrow(20);
1549        %cube([100,100,0.1],center=true);
1550    }
1551}
1552```
1553
1554When `extent=false`, then the anchor point will be the furthest intersection of the VNF with
1555the anchor ray from the origin. The orientation of the anchor point will be the normal of the
1556face at the intersection.  If the intersection is at an edge or corner, then the orientation
1557will bisect the angles between the faces.
1558
1559```openscad-VPD=1250
1560include <BOSL2/std.scad>
1561module stellate_cube(s=100, anchor=CENTER, spin=0, orient=UP) {
1562    s2 = 3 * s;
1563    verts = [
1564        [0,0,-s2*sqrt(2)/2],
1565        each down(s/2, p=path3d(square(s,center=true))),
1566        each zrot(45, p=path3d(square(s2,center=true))),
1567        each up(s/2, p=path3d(square(s,center=true))),
1568        [0,0,s2*sqrt(2)/2]
1569    ];
1570    faces = [
1571        [0,2,1], [0,3,2], [0,4,3], [0,1,4],
1572        [1,2,6], [1,6,9], [6,10,9], [2,10,6],
1573        [1,5,4], [1,9,5], [9,12,5], [5,12,4],
1574        [4,8,3], [4,12,8], [12,11,8], [11,3,8],
1575        [2,3,7], [3,11,7], [7,11,10], [2,7,10],
1576        [9,10,13], [10,11,13], [11,12,13], [12,9,13]
1577    ];
1578    vnf = [verts, faces];
1579    attachable(anchor,spin,orient, vnf=vnf, extent=false) {
1580        vnf_polyhedron(vnf);
1581        children();
1582    }
1583}
1584stellate_cube() show_anchors(50);
1585```
1586
1587```openscad-3D
1588include <BOSL2/std.scad>
1589$fn=32;
1590R = difference(circle(10), right(2, circle(9)));
1591linear_sweep(R,height=10,atype="hull")
1592    attach(RIGHT) anchor_arrow();
1593```
1594
1595
1596## Making Named Anchors
1597While vector anchors are often useful, sometimes there are logically extra attachment points that
1598aren't on the perimeter of the shape.  This is what named string anchors are for.  For example,
1599the `teardrop()` shape uses a cylindrical geometry for it's vector anchors, but it also provides
1600a named anchor "cap" that is at the tip of the hat of the teardrop shape.
1601
1602Named anchors are passed as an array of `named_anchor()`s to the `anchors=` argument of `attachable()`.
1603The `named_anchor()` call takes a name string, a positional point, an orientation vector, and a spin.
1604The name is the name of the anchor.  The positional point is where the anchor point is at.  The
1605orientation vector is the direction that a child attached at that anchor point should be oriented.
1606The spin is the number of degrees that an attached child should be rotated counter-clockwise around
1607the orientation vector.  Spin is optional, and defaults to 0.
1608
1609To make a simple attachable shape similar to a `teardrop()` that provides a "cap" anchor, you may
1610define it like this:
1611
1612```openscad-3D
1613include <BOSL2/std.scad>
1614module raindrop(r, thick, anchor=CENTER, spin=0, orient=UP) {
1615    anchors = [
1616        named_anchor("cap", [0,r/sin(45),0], BACK, 0)
1617    ];
1618    attachable(anchor,spin,orient, r=r, l=thick, anchors=anchors) {
1619        linear_extrude(height=thick, center=true) {
1620            circle(r=r);
1621            back(r*sin(45)) zrot(45) square(r, center=true);
1622        }
1623        children();
1624    }
1625}
1626raindrop(r=25, thick=20, anchor="cap");
1627```
1628
1629If you want multiple named anchors, just add them to the list of anchors:
1630
1631```openscad-FlatSpin,VPD=150
1632include <BOSL2/std.scad>
1633module raindrop(r, thick, anchor=CENTER, spin=0, orient=UP) {
1634    anchors = [
1635        named_anchor("captop", [0,r/sin(45), thick/2], BACK+UP,   0),
1636        named_anchor("cap",    [0,r/sin(45), 0      ], BACK,      0),
1637        named_anchor("capbot", [0,r/sin(45),-thick/2], BACK+DOWN, 0)
1638    ];
1639    attachable(anchor,spin,orient, r=r, l=thick, anchors=anchors) {
1640        linear_extrude(height=thick, center=true) {
1641            circle(r=r);
1642            back(r*sin(45)) zrot(45) square(r, center=true);
1643        }
1644        children();
1645    }
1646}
1647raindrop(r=15, thick=10) show_anchors();
1648```
1649
1650Sometimes the named anchor you want to add may be at a point that is reached through a complicated
1651set of translations and rotations.  One quick way to calculate that point is to reproduce those
1652transformations in a transformation matrix chain.  This is simplified by how you can use the
1653function forms of almost all the transformation modules to get the transformation matrices, and
1654chain them together with matrix multiplication.  For example, if you have:
1655
1656```
1657scale([1.1, 1.2, 1.3]) xrot(15) zrot(25) right(20) sphere(d=1);
1658```
1659
1660and you want to calculate the center point of the sphere, you can do it like:
1661
1662```
1663sphere_pt = apply(
1664    scale([1.1, 1.2, 1.3]) * xrot(15) * zrot(25) * right(20),
1665    [0,0,0]
1666);
1667```
1668
1669
1670## Overriding Standard Anchors
1671
1672Sometimes you may want to use the standard anchors but override some
1673of them.  Returning to the square barebell example above, the anchors
1674at the right and left sides are on the cubes at each end, but the
1675anchors at x=0 are in floating in space.  For prismoidal/cubic anchors
1676in 3D and trapezoidal/rectangular anchors in 2D we can override a single anchor by
1677specifying the override option and giving the anchor that is being
1678overridden, and then the replacement in the form
1679`[position, direction, spin]`.  Most often you will only want to
1680override the position.  If you omit the other list items then the
1681value drived from the standard anchor will be used. Below we override
1682position of the FWD anchor:
1683
1684```
1685module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
1686    override = [
1687                 [FWD,  [[0,-s/8,0]]]
1688               ];
1689    attachable(anchor,spin,orient, size=[s*3,s,s],override=override) {
1690        union() {
1691            xcopies(2*s) cube(s, center=true);
1692            xcyl(h=2*s, d=s/4);
1693        }
1694        children();
1695    }
1696}
1697cubic_barbell(100) show_anchors(60);
1698```
1699
1700Note how the FWD anchor is now rooted on the cylindrical portion.  If
1701you wanted to also change its direction and spin you could do it like
1702this:
1703
1704```
1705module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
1706    override = [
1707                 [FWD,  [[0,-s/8,0], FWD+LEFT, 225]]
1708               ];
1709    attachable(anchor,spin,orient, size=[s*3,s,s],override=override) {
1710        union() {
1711            xcopies(2*s) cube(s, center=true);
1712            xcyl(h=2*s, d=s/4);
1713        }
1714        children();
1715    }
1716}
1717cubic_barbell(100) show_anchors(60);
1718```
1719
1720In the above example we give three values for the override.  As
1721before, the first one places the anchor on the cylinder.  We have
1722added the second entry which points the anchor off to the left.
1723The third entry gives a spin override, whose effect is shown by the
1724position of the red flag on the arrow.  If you want to override all of
1725the x=0 anchors to be on the cylinder, with their standard directions,
1726you can do that by supplying a list: 
1727```
1728module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
1729    override = [
1730                 for(j=[-1:1:1], k=[-1:1:1])
1731                   if ([j,k]!=[0,0]) [[0,j,k], [s/8*unit([0,j,k])]]
1732               ];
1733    attachable(anchor,spin,orient, size=[s*3,s,s],override=override) {
1734        union() {
1735            xcopies(2*s) cube(s, center=true);
1736            xcyl(h=2*s, d=s/4);
1737        }
1738        children();
1739    }
1740}
1741cubic_barbell(100) show_anchors(30);
1742```
1743
1744Now all of the anchors in the middle are all rooted to the cylinder.  Another
1745way to do the same thing is to use a function literal for override.
1746It will be called with the anchor as its argument and needs to return undef to just use
1747the default, or a `[position, direction, spin]` triple to override the
1748default.  As before, you can omit values to keep their default.
1749Here is the same example using a function literal for the override:
1750
1751```
1752module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
1753    override = function (anchor) 
1754          anchor.x!=0 || anchor==CTR ? undef  // Keep these
1755        : [s/8*unit(anchor)];
1756    attachable(anchor,spin,orient, size=[s*3,s,s],override=override) {
1757        union() {
1758            xcopies(2*s) cube(s, center=true);
1759            xcyl(h=2*s, d=s/4);
1760        }
1761        children();
1762    }
1763}
1764cubic_barbell(100) show_anchors(30);
1765```